package jk.fox.transport.netty.client;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.AttributeKey;
import jk.fox.common.RpcDict;
import jk.fox.common.payload.message.Message;
import jk.fox.common.payload.message.MessageDispatcher;
import org.apache.log4j.Logger;

import java.net.InetSocketAddress;

/**
 * @author cuichao
 * @Description: PingEmitter
 * @Date: create in 2020/4/22 21:30
 * 客户端每隔一定空闲写时间ping
 *
 */
public class PingEmitter extends SimpleChannelInboundHandler<RpcDict.HeartBeat> {

    private static Logger logger = Logger.getLogger(PingEmitter.class);

    private final int reset = 0;

    private MessageDispatcher dispatcher;

    public PingEmitter (MessageDispatcher dispatcher){
        this.dispatcher = dispatcher;
    }

    /**
     * failover
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        logger.warn(ctx.channel().remoteAddress()+": channelInactive");
        InetSocketAddress address = (InetSocketAddress)ctx.channel().remoteAddress();
        Message message = new Message();
        message.setStatus(RpcDict.MessageStatus.Invalid);
        message.payload(RpcDict.SERVER_ADDRESS,address);
        dispatcher.dispatch(message);
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, RpcDict.HeartBeat msg) throws Exception {
        if(msg == RpcDict.HeartBeat.ping){
            logger.debug(ctx.channel().remoteAddress()+" has accept ping "+ctx.channel().localAddress());
            ctx.channel().writeAndFlush(RpcDict.HeartBeat.pong);
        }
        if(msg == RpcDict.HeartBeat.pong){
            ctx.channel().attr(AttributeKey.valueOf(RpcDict.PING_COUNTER)).set(reset);
        }
    }


    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if(!(evt instanceof IdleStateEvent)){
           super.userEventTriggered(ctx,evt);
        }

        IdleStateEvent event = (IdleStateEvent)evt;
        if (event.state() == IdleState.WRITER_IDLE) {
            int pingCount = (int)ctx.channel().attr(AttributeKey.valueOf(RpcDict.PING_COUNTER)).get();
            pingCount++;
            ctx.channel().attr(AttributeKey.valueOf(RpcDict.PING_COUNTER)).set(pingCount);
            if(pingCount > 3 ){
                logger.info(ctx.channel().remoteAddress()+"is time out, channel is close");
                ctx.disconnect();
                ctx.close();
            }
           ctx.writeAndFlush(RpcDict.HeartBeat.ping);
           logger.debug(ctx.channel().localAddress()+" has ping "+ctx.channel().remoteAddress());
        }
    }

}
